#!/bin/bash
# 网卡绑定配置脚本
# 支持RHEL/CentOS 7+, Ubuntu 16.04+, Debian 9+等主流Linux发行版
# 需要root权限执行

# 版本信息
VERSION="1.1"
RELEASE_DATE="2023-11-15"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

# 检查root权限
if [ "$(id -u)" != "0" ]; then
    echo -e "${RED}错误：此脚本必须以root权限运行！${NC}"
    exit 1
fi

# 显示标题
echo -e "${GREEN}网卡绑定配置脚本 v${VERSION} (${RELEASE_DATE})${NC}"
echo "----------------------------------------"

# 检测发行版
detect_distro() {
    if [ -f /etc/redhat-release ]; then
        DISTRO="RHEL/CentOS"
        CONF_DIR="/etc/sysconfig/network-scripts"
    elif [ -f /etc/lsb-release ] && grep -q "Ubuntu" /etc/lsb-release; then
        DISTRO="Ubuntu"
        CONF_DIR="/etc/network"
    elif [ -f /etc/debian_version ]; then
        DISTRO="Debian"
        CONF_DIR="/etc/network"
    else
        DISTRO="Unknown"
        CONF_DIR="/etc/sysconfig/network-scripts" # 默认使用RedHat风格
    fi
    echo -e "${YELLOW}检测到系统: ${DISTRO}${NC}"
}

# 加载bonding模块
load_bonding() {
    if ! lsmod | grep -q bonding; then
        echo -e "${YELLOW}正在加载bonding模块...${NC}"
        modprobe bonding
        if [ $? -ne 0 ]; then
            echo -e "${RED}错误：无法加载bonding模块！${NC}"
            exit 1
        fi
    fi
    
    # 确保模块开机加载
    if [ ! -f /etc/modules-load.d/bonding.conf ]; then
        echo "bonding" > /etc/modules-load.d/bonding.conf
    fi
}

# 验证IP格式
validate_ip() {
    local ip_cidr=$1
    if [[ ! "$ip_cidr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]{1,2}$ ]]; then
        echo -e "${RED}错误：IP格式无效，请使用CIDR格式（如192.168.1.100/24）！${NC}"
        return 1
    fi
    
    local ip=${ip_cidr%/*}
    local prefix=${ip_cidr#*/}
    
    if [ "$prefix" -lt 8 ] || [ "$prefix" -gt 32 ]; then
        echo -e "${RED}错误：网络前缀必须在8-32之间！${NC}"
        return 1
    fi
    
    IFS='.' read -r -a octets <<< "$ip"
    for octet in "${octets[@]}"; do
        if [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then
            echo -e "${RED}错误：IP地址无效！${NC}"
            return 1
        fi
    done
    
    return 0
}

# 验证网关格式
validate_gateway() {
    local gateway=$1
    if [[ ! "$gateway" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
        echo -e "${RED}错误：网关格式无效！${NC}"
        return 1
    fi
    
    IFS='.' read -r -a octets <<< "$gateway"
    for octet in "${octets[@]}"; do
        if [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then
            echo -e "${RED}错误：网关地址无效！${NC}"
            return 1
        fi
    done
    
    return 0
}

# 重启网络服务
restart_network() {
    echo -e "${YELLOW}正在应用网络配置...${NC}"
    
    case $DISTRO in
        "RHEL/CentOS")
            if systemctl is-active NetworkManager &>/dev/null; then
                systemctl restart NetworkManager
            else
                systemctl restart network
            fi
            ;;
        "Ubuntu"|"Debian")
            if systemctl is-active --quiet networking; then
                systemctl restart networking
            else
                systemctl restart systemd-networkd
            fi
            ;;
        *)
            # 尝试所有可能的网络服务
            if systemctl restart NetworkManager &>/dev/null; then
                echo "使用NetworkManager重启网络"
            elif systemctl restart network &>/dev/null; then
                echo "使用network服务重启网络"
            elif systemctl restart networking &>/dev/null; then
                echo "使用networking服务重启网络"
            elif systemctl restart systemd-networkd &>/dev/null; then
                echo "使用systemd-networkd重启网络"
            else
                echo -e "${RED}错误：无法确定网络服务，请手动重启网络！${NC}"
                return 1
            fi
            ;;
    esac
    
    if [ $? -ne 0 ]; then
        echo -e "${RED}错误：网络服务重启失败！${NC}"
        return 1
    fi
    
    return 0
}

# 备份原有配置
backup_config() {
    local iface=$1
    local timestamp=$(date +%Y%m%d%H%M%S)
    
    if [ -f "$CONF_DIR/ifcfg-$iface" ]; then
        cp "$CONF_DIR/ifcfg-$iface" "$CONF_DIR/ifcfg-$iface.bak.$timestamp"
    elif [ -f "$CONF_DIR/interfaces" ]; then
        cp "$CONF_DIR/interfaces" "$CONF_DIR/interfaces.bak.$timestamp"
    fi
}

# 配置RedHat风格bond
configure_redhat_bond() {
    # 1. 创建bond配置文件
    cat > "$CONF_DIR/ifcfg-$BOND_NAME" <<EOF
DEVICE=$BOND_NAME
NAME=$BOND_NAME
TYPE=Bond
BONDING_MASTER=yes
IPADDR=$IP_ADDR
PREFIX=$PREFIX
ONBOOT=yes
BOOTPROTO=none
BONDING_OPTS="mode=$BOND_MODE miimon=100 xmit_hash_policy=layer3+4"
EOF

    # 添加网关（如果提供）
    if [ -n "$GATEWAY" ]; then
        echo "GATEWAY=$GATEWAY" >> "$CONF_DIR/ifcfg-$BOND_NAME"
    fi

    # 2. 配置从接口
    for slave in "${SLAVES[@]}"; do
        backup_config "$slave"
        
        cat > "$CONF_DIR/ifcfg-$slave" <<EOF
DEVICE=$slave
NAME=$slave
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
MASTER=$BOND_NAME
SLAVE=yes
EOF
    done
}

# 配置Debian风格bond
configure_debian_bond() {
    # 备份原有配置
    backup_config "interfaces"
    
    # 添加bond配置到interfaces文件
    if ! grep -q "iface $BOND_NAME" "$CONF_DIR/interfaces" 2>/dev/null; then
        echo -e "\nauto $BOND_NAME" >> "$CONF_DIR/interfaces"
        echo "iface $BOND_NAME inet static" >> "$CONF_DIR/interfaces"
        echo "  address $IP_ADDR/$PREFIX" >> "$CONF_DIR/interfaces"
        [ -n "$GATEWAY" ] && echo "  gateway $GATEWAY" >> "$CONF_DIR/interfaces"
        echo "  bond-mode $BOND_MODE" >> "$CONF_DIR/interfaces"
        echo "  bond-miimon 100" >> "$CONF_DIR/interfaces"
        echo "  bond-slaves none" >> "$CONF_DIR/interfaces"
    fi
    
    # 配置从接口
    for slave in "${SLAVES[@]}"; do
        backup_config "$slave"
        
        if ! grep -q "iface $slave" "$CONF_DIR/interfaces" 2>/dev/null; then
            echo -e "\nauto $slave" >> "$CONF_DIR/interfaces"
            echo "iface $slave inet manual" >> "$CONF_DIR/interfaces"
            echo "  bond-master $BOND_NAME" >> "$CONF_DIR/interfaces"
        fi
    done
    
    # 更新bond-slaves
    slaves_list=$(IFS=" "; echo "${SLAVES[*]}")
    sed -i "/bond-slaves/c\  bond-slaves $slaves_list" "$CONF_DIR/interfaces"
}

# 主函数
main() {
    # 检测发行版
    detect_distro
    
    # 加载bonding模块
    load_bonding
    
    # 输入bond名称
    read -p "请输入bond接口名称 [默认: bond0]: " BOND_NAME
    BOND_NAME=${BOND_NAME:-bond0}
    
    # 检查bond是否已存在
    if ip link show "$BOND_NAME" &>/dev/null; then
        echo -e "${YELLOW}警告：接口 $BOND_NAME 已存在，将继续配置它${NC}"
    fi
    
    # 输入绑定模式
    echo -e "${GREEN}支持的bond模式:${NC}"
    echo "0 - balance-rr (轮询, 负载均衡)"
    echo "1 - active-backup (主备, 冗余)"
    echo "2 - balance-xor (XOR, 负载均衡)"
    echo "3 - broadcast (广播, 冗余)"
    echo "4 - 802.3ad (动态链路聚合, 需要交换机支持)"
    echo "5 - balance-tlb (适配传输负载均衡)"
    echo "6 - balance-alb (适配负载均衡)"
    read -p "请选择bond模式 [0-6, 默认: 1]: " BOND_MODE
    BOND_MODE=${BOND_MODE:-1}
    
    # 验证模式
    if ! [[ "$BOND_MODE" =~ ^[0-6]$ ]]; then
        echo -e "${RED}错误：无效的bond模式！${NC}"
        exit 1
    fi
    
    # 输入网卡列表
    read -p "请输入要绑定的物理网卡（空格分隔，如: eth0 eth1）: " -a SLAVES
    
    # 验证网卡存在
    for iface in "${SLAVES[@]}"; do
        if ! ip link show "$iface" &>/dev/null; then
            echo -e "${RED}错误：网卡 $iface 不存在！${NC}"
            exit 1
        fi
    done
    
    # 输入IP配置
    while true; do
        read -p "请输入IP地址（CIDR格式，如: 192.168.1.100/24）: " IP_CIDR
        validate_ip "$IP_CIDR"
        if [ $? -eq 0 ]; then
            break
        fi
    done
    
    # 提取IP和前缀
    IP_ADDR=${IP_CIDR%/*}
    PREFIX=${IP_CIDR#*/}
    
    # 输入网关（可选）
    while true; do
        read -p "请输入网关（留空则不配置）: " GATEWAY
        if [ -z "$GATEWAY" ]; then
            break
        fi
        validate_gateway "$GATEWAY"
        if [ $? -eq 0 ]; then
            break
        fi
    done
    
    # 根据发行版选择配置方式
    case $DISTRO in
        "RHEL/CentOS")
            configure_redhat_bond
            ;;
        "Ubuntu"|"Debian")
            configure_debian_bond
            ;;
        *)
            # 尝试RedHat风格配置
            if [ -d "$CONF_DIR" ]; then
                configure_redhat_bond
            else
                # 尝试Debian风格配置
                CONF_DIR="/etc/network"
                if [ -d "$CONF_DIR" ]; then
                    configure_debian_bond
                else
                    echo -e "${RED}错误：无法确定网络配置目录！${NC}"
                    exit 1
                fi
            fi
            ;;
    esac
    
    # 重启网络服务
    restart_network
    
    # 显示结果
    if [ $? -eq 0 ]; then
        echo -e "${GREEN}配置已成功应用！${NC}"
        echo -e "${YELLOW}绑定接口状态:${NC}"
        if cat /proc/net/bonding/$BOND_NAME; then
            echo -e "\n${GREEN}绑定配置完成！${NC}"
        else
            echo -e "${RED}警告：无法读取绑定状态，请检查配置${NC}"
        fi
    else
        echo -e "${RED}错误：网络配置应用失败！${NC}"
        echo -e "${YELLOW}请检查以下文件:${NC}"
        [ -f "$CONF_DIR/ifcfg-$BOND_NAME" ] && echo "$CONF_DIR/ifcfg-$BOND_NAME"
        [ -f "$CONF_DIR/interfaces" ] && echo "$CONF_DIR/interfaces"
        for slave in "${SLAVES[@]}"; do
            [ -f "$CONF_DIR/ifcfg-$slave" ] && echo "$CONF_DIR/ifcfg-$slave"
        done
    fi
}

# 执行主函数
main